package com.lyj.threads;

import java.util.concurrent.*;

/**
 * 自定义线程池
 * 使用阻塞队列
 */
public class CustomerThreadPoolExecutor {

    /**
     * 线程池对象
     */
    private ThreadPoolExecutor pool = null;


    /**
     * 超过 corePoolSize 线程数量的线程最大空闲时间
     */
    long keepAliveTime = 2;
    //以秒为时间单位
    TimeUnit unit = TimeUnit.SECONDS;

    public CustomerThreadPoolExecutor() {
    }

    /**
     * 通过线程池运行的流程图可以看到，当提交任务后，线程池首先会检查当前线程数，如果此时线程数小于核心线程数，比如最开始线程数量为 0，则新建线程并执行任务，随着任务的不断增加，线程数会逐渐增加并达到核心线程数，此时如果仍有任务被不断提交，就会被放入 workQueue 任务队列中，等待核心线程执行完当前任务后重新从 workQueue 中提取正在等待被执行的任务。
     * 此时，假设我们的任务特别的多，已经达到了 workQueue 的容量上限，这时线程池就会启动后备力量，也就是 maximumPoolSize 最大线程数，线程池会在 corePoolSize 核心线程数的基础上继续创建线程来执行任务，假设任务被不断提交，线程池会持续创建线程直到线程数达到 maximumPoolSize 最大线程数，如果依然有任务被提交，这就超过了线程池的最大处理能力，这个时候线程池就会拒绝这些任务，我们可以看到实际上任务进来之后，线程池会逐一判断 corePoolSize、workQueue、maximumPoolSize，如果依然不能满足需求，则会拒绝任务。
     * 原文链接：https://blog.csdn.net/u010132124/article/details/115110935
     * @param corePoolSize 核心线程数
     * @param maximunPoolSize 最大线程数
     * @param workQueueSize 任务队列大小
     * @param threadFactory  线程工厂，用来创建新线程
     * @param rjHandler 拒绝策略
     */
    public CustomerThreadPoolExecutor(int corePoolSize, int maximunPoolSize, int workQueueSize,ThreadFactory threadFactory, RejectedExecutionHandler rjHandler) {
        pool = new ThreadPoolExecutor(corePoolSize,//核心线程数
                maximunPoolSize,//最大线程数
                keepAliveTime,//空闲线程的存活时间
                unit,//空闲时间单位
                new LinkedBlockingDeque(workQueueSize),// 无界任务队列
                threadFactory == null ? new MySelfThreadFactory() : threadFactory,
                rjHandler == null ? new ThreadPoolExecutor.AbortPolicy() : rjHandler);// 默认拒绝策略，拒绝任务并抛出任务
    }


    /**
     * 指定拒绝策略：如国队列已满，等待指定时间后再往缓存队列存档
     * @param corePoolSize
     * @param maximunPoolSize
     * @param workQueueSize
     */
    public CustomerThreadPoolExecutor(int corePoolSize, int maximunPoolSize, int workQueueSize) {
        pool = new ThreadPoolExecutor(corePoolSize,//核心线程数
                maximunPoolSize,//最大线程数
                keepAliveTime,//空闲线程的存活时间
                unit,//空闲时间单位
                new LinkedBlockingDeque(workQueueSize),
                new MySelfThreadFactory(),
                new MySelfRejectExecutionHandler());
    }

    /**
     * 线程池初始化方法
     * @param corePoolSize 核心线程池大小
     * @param maxPoolSize 最大线程池大小
     * @param workQueueSize 任务队列大小
     */
    public void initWait(int corePoolSize, int maxPoolSize , int workQueueSize ) {
        if (pool == null) {
            pool = new ThreadPoolExecutor(corePoolSize,
                    maxPoolSize,
                    keepAliveTime,
                    unit,
                    new LinkedBlockingDeque(workQueueSize),
                    new MySelfThreadFactory(),
                    new MySelfRejectExecutionHandler());
        }
    }
    public void destory() {
        if (pool != null) {
            pool.shutdown();
        }
    }

    /**
     * 获取自定义线程池
     * @return
     */
    public ThreadPoolExecutor getCommonThreadPoolExecutor() {
        return this.pool;
    }
    /*
    workQueue是线程池的任务队列，作为一种缓冲机制，线程池会把当下没有处理的任务放入任务队列中，由于多线程同时从任务队列中获取任务是并发场景，此时就需要任务队列满足线程安全的要求，所以线程池中任务队列采用 BlockingQueue 来保障线程安全。常用的队列主要有以下几种：
        1、LinkedBlockingQueue(无界)
        LinkedBlockingQueue是一个无界缓存等待队列。当前执行的线程数量达到corePoolSize的数量时，剩余的元素会在阻塞队列里等待。（所以在使用此阻塞队列时maximumPoolSizes就相当于无效了），每个线程完全独立于其他线程。生产者和消费者使用独立的锁来控制数据的同步，即在高并发的情况下可以并行操作队列中的数据。
        这个队列需要注意的是，虽然通常称其为一个无界队列，但是可以人为指定队列大小，而且由于其用于记录队列大小的参数是int类型字段，所以通常意义上的无界其实就是队列长度为 Integer.MAX_VALUE，且在不指定队列大小的情况下也会默认队列大小为 Integer.MAX_VALUE。

        2、SynchronousQueue
        SynchronousQueue没有容量，是无缓冲等待队列，是一个不存储元素的阻塞队列，会直接将任务交给消费者，必须等队列中的添加元素被消费后才能继续添加新的元素。拥有公平（FIFO）和非公平(LIFO)策略，使用SynchronousQueue阻塞队列一般要求maximumPoolSizes为无界(Integer.MAX_VALUE)，避免线程拒绝执行操作。

        3、ArrayBlockingQueue
        ArrayBlockingQueue是一个有界缓存等待队列，可以指定缓存队列的大小，当正在执行的线程数等于corePoolSize时，多余的元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行，当ArrayBlockingQueue已满时，加入ArrayBlockingQueue失败，会开启新的线程去执行，当线程数已经达到最大的maximumPoolSizes时，再有新的元素尝试加入ArrayBlockingQueue时会报错。

        4、DelayedWorkQueue
        DelayedWorkQueue 的特点是内部元素并不是按照放入的时间排序，而是会按照延迟的时间长短对任务进行排序，内部采用的是“堆”的数据结构。之所以线程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 选择 DelayedWorkQueue，是因为它们本身正是基于时间执行任务的，而延迟队列正好可以把任务按时间进行排序，方便任务的执行。

    RejectedExecutionHandler handler
        在使用线程池并且使用有界队列（如ArrayBlockingQueue）的时候，如果队列满了，任务添加到线程池的时候就会有问题，那么这些溢出的任务，ThreadPoolExecutor为我们提供了拒绝任务的处理方式，以便在必要的时候按照我们的策略来拒绝任务，线程池拒绝任务的时机有以下两种：
        第一种情况是当我们调用 shutdown 等方法关闭线程池后，即便此时可能线程池内部依然有没执行完的任务正在执行，但是由于线程池已经关闭，此时如果再向线程池内提交任务，就会遭到拒绝。
        第二种情况是线程池没有能力继续处理新提交的任务，也就是工作已经非常饱和的时候。
        线程池任务拒绝策略实现了 RejectedExecutionHandler 接口，JDK 中自带了四种任务拒绝策略。
        分别是AbortPolicy、DiscardPolicy、DiscardOldestPolicy、CallerRunsPolicy。其中AbortPolicy是ThreadPoolExecutor默认使用。
————————————————
     */
}
